import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository, Like, DeepPartial } from 'typeorm';
import { User as UserEntity } from '@/entity/User';
import { UserRole as UserRoleEntity } from '@/entity/UserRole';
import { CommonService } from '../common/common.service';
import { PaginationConfig } from '@/types';
import { randomUUID } from 'crypto';
import { ResponseCode } from '@/utils/ResponseCode';
import { Request as HttpRequest } from 'express';

@Injectable()
export class UserService extends CommonService<UserEntity> {
  constructor(
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>,
    private dataSource: DataSource,
  ) {
    super(userRepository);
  }

  // 带出密码
  async findOneBy(whereOption: Partial<UserEntity> | Partial<UserEntity>[]) {
    return await this.userRepository.findOne({
      where: whereOption,
      select: [
        'id',
        'username',
        'password',
        'nickname',
        'email',
        'avatarImage',
        'createTime',
        'updateTime',
      ],
    });
  }

  // 更新信息
  async updateUserWithRole(
    userId,
    toUpdateUser: Partial<UserEntity>,
    paramsRoleIds: number[],
  ) {
    const userEntity = await this.findUserWithRole({ id: userId });
    const oldRoleIds = userEntity.userRoles.map((item) => item.role.id);
    const oldRoleIdsMap = oldRoleIds.reduce((prev, roleId) => {
      prev[roleId] = true;
      return prev;
    }, {} as { [key: number]: boolean });
    const paramsRoleIdsMap = paramsRoleIds.reduce((prev, roleId) => {
      prev[roleId] = true;
      return prev;
    }, {} as { [key: number]: boolean });
    // 旧的ids 比 新的ids 多出来的就是删除的
    const toDeleteRoleIds = oldRoleIds.filter(
      (permissionId) => !paramsRoleIdsMap[permissionId],
    );
    const toAddRoleIds = paramsRoleIds.filter(
      (permissionId) => !oldRoleIdsMap[permissionId],
    );
    // 开启事务
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    try {
      // 开启事务
      await queryRunner.startTransaction();
      const allPromise = [];
      // 更新当前用户的信息，需要避免写入密码
      if (Object.keys(toUpdateUser).length) {
        allPromise.push(
          await queryRunner.manager
            .getRepository(UserEntity)
            .createQueryBuilder()
            .update()
            .set({ ...toUpdateUser, password: undefined })
            .where('id = :userId', { userId })
            .execute(),
        );
      }
      if (toDeleteRoleIds.length) {
        allPromise.push(
          queryRunner.manager
            .getRepository(UserRoleEntity)
            .createQueryBuilder()
            .delete()
            .where('userId = :userId', {
              userId,
            })
            .andWhere('roleId IN (:...roleIds)', {
              roleIds: toDeleteRoleIds,
            })
            .execute(),
        );
      }
      // 插入多个
      if (toAddRoleIds.length) {
        allPromise.push(
          queryRunner.manager
            .getRepository(UserRoleEntity)
            .createQueryBuilder()
            .insert()
            .values(
              toAddRoleIds.map((roleId) => ({
                userId,
                roleId,
              })),
            )
            .execute(),
        );
      }
      // 结束事务
      await queryRunner.commitTransaction();
    } catch (error) {
      // 发生错误回滚
      await queryRunner.rollbackTransaction();
      throw error;
    }
  }

  // 注册
  async createUser(roleIds: number[], params: Partial<UserEntity>) {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    // 开启事务
    await queryRunner.startTransaction();
    let userEntity: UserEntity | null = null;
    try {
      // 先创建用户，注意创建用户生成随机的密码，用户需要根据邮箱去修改
      userEntity = await queryRunner.manager
        .getRepository(UserEntity)
        .save({ ...params, password: randomUUID() });
      // 创建用户权限记录，并 自动 校验是否存在 roleId 对应的记录
      await queryRunner.manager
        .getRepository(UserRoleEntity)
        .createQueryBuilder()
        .insert()
        .values(roleIds.map((roleId) => ({ roleId, userId: userEntity.id })))
        .execute();
      // 结束事务
      await queryRunner.commitTransaction();
    } catch (error) {
      // 发生错误回滚
      await queryRunner.rollbackTransaction();
      throw error;
    }
    // 释放
    await queryRunner.release();
    return userEntity;
  }

  // 获取 角色列表
  async findUserWithRole(findWhere: Partial<UserEntity>) {
    return await this.userRepository.findOne({
      where: findWhere,
      join: {
        alias: 'user',
        leftJoinAndSelect: {
          userRole: 'user.userRoles',
          role: 'userRole.role',
        },
      },
    });
  }

  // 获取用户所有的角色和权限
  async findUserWithRolePermission(findWhere: Partial<UserEntity>) {
    return await this.userRepository.findOne({
      where: findWhere,
      join: {
        alias: 'user',
        leftJoinAndSelect: {
          userRole: 'user.userRoles',
          role: 'userRole.role',
          rolePermission: 'role.rolePermissions',
          permission: 'rolePermission.permission',
        },
      },
    });
  }

  async findAllAndCount(
    findOption: DeepPartial<UserEntity>,
    pagination: PaginationConfig,
  ) {
    const [list, total] = await this.userRepository.findAndCount({
      where: {
        // 字符串不为空
        username: findOption.username
          ? Like(`%${findOption.username}%`)
          : undefined,
        nickname: findOption.nickname
          ? Like(`%${findOption.nickname}%`)
          : undefined,
        email: findOption.email ? Like(`%${findOption.email}%`) : undefined,
        // id 肯定为正整数
        id: findOption.id ? findOption.id : undefined,
      },
      skip: pagination.pageSize * (pagination.current - 1),
      take: pagination.pageSize,
    });
    return {
      list,
      total,
    };
  }

  /**
   *
   * @param routePath
   * @param username
   * @returns 当前用户是否含有该接口访问权限
   * @description 用户身份接口鉴权
   */
  async verifyPermissionForAPI(routePath: string, username: string) {
    if (!username) {
      throw new Error('用户未登入');
    }
    let userEntity = await this.findOneBy({ username });
    if (!userEntity) {
      throw new Error('鉴权用户不存在');
    }
    userEntity = await this.findUserWithRolePermission({
      username: username,
    });

    // 使用map是为了防止有相同的 apiStr
    const apiListSet: Set<string> = new Set();
    for (const { role: roleEntity } of userEntity.userRoles) {
      // 如果存在超级管理员角色直接跳过判断
      if (roleEntity.isSuper) return true;
      // 否则一个个判断角色是否存在接口权限
      for (const { permission } of roleEntity.rolePermissions) {
        // 是个被parse过的对象，已经被typeorm处理好了
        const { apiList } = permission;
        (apiList as string[]).forEach((apiStr) => {
          apiListSet.add(apiStr);
        });
      }
    }
    // 是否包含权限
    return apiListSet.has(routePath);
  }

  // 简化controller逻辑
  async verifyPermission(request: HttpRequest, username: string) {
    const { method, route } = request;
    const { path } = route;
    const routePath = `${method}_${path}`;
    const hasPermission = await this.verifyPermissionForAPI(
      // 当前的路由路径
      routePath,
      username,
    );
    if (!hasPermission) {
      throw new HttpException(
        {
          message: '用户无权限访问该接口',
          code: ResponseCode.Fail,
          data: null,
        },
        HttpStatus.FORBIDDEN,
      );
    }
  }
}
